home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Misc / RZToDoList / Source / ToDoItem.m < prev    next >
Encoding:
Text File  |  1995-06-12  |  9.0 KB  |  421 lines

  1. /* 
  2.  * ToDoItem - implementation of ToDoItem, the basic unit of information 
  3.  *     for the ToDoList program.  The method -adjustPriority implements
  4.  *     the dynamic priority mechanism.
  5.  *
  6.  * You may freely copy, distribute and reuse the code in this example.
  7.  * This code is provided AS IS without warranty of any kind, expressed 
  8.  * or implied, as to its fitness for any particular use.
  9.  *
  10.  * Copyright 1995 Ralph Zazula (rzazula@next.com).  All Rights Reserved.
  11.  *
  12.  */
  13.  
  14. #import "ToDoItem.h"
  15. #import "RZ.h"
  16. #import <appkit/appkit.h>
  17. #import <math.h>
  18.  
  19. typedef struct _toDoItemFlags {
  20. #ifdef __BIG_ENDIAN__
  21.     unsigned int type:8;
  22.     unsigned int private:1;
  23.     unsigned int completed:1;
  24.     unsigned int _RESERVED:22;
  25. #else
  26.     unsigned int _RESERVED:22;
  27.     unsigned int completed:1;
  28.     unsigned int private:1;
  29.     unsigned int type:8;
  30. #endif
  31. } toDoItemFlags;
  32.  
  33. #define _flags ((toDoItemFlags *)_reservedToDoItem1)
  34.  
  35. @implementation ToDoItem
  36.  
  37. + initialize
  38. {
  39.     if([self class] == [ToDoItem class]) {
  40.         [self setVersion:4];
  41.     }
  42.     return self;
  43. }
  44.  
  45. - initSubject:(char *)subj startDate:(long)start dueDate:(long)due
  46.     completeDate:(long)completed type:(char)type isPrivate:(BOOL)privateFlag
  47.     isCompleted:(BOOL)completeFlag data:(char *)buf dataLen:(int)len
  48. {
  49.     if(self = [super init]) {
  50.  
  51.         _flags = (toDoItemFlags *)calloc(1, sizeof(toDoItemFlags));
  52.  
  53.         [self setSubject:subj];
  54.         [self setStartDate:start];
  55.         [self setDueDate:due];
  56.         [self setType:type];
  57.         [self setPrivate:privateFlag];
  58.         
  59.         /* don't want auto-calc of completion so don't use setCompleted: */
  60.         dateCompleted = completed ? completed : 0;
  61.         _flags->completed = completeFlag;
  62.         
  63.         [self setData:buf];
  64.     }
  65.     return self;
  66. }
  67.     
  68. - initFromItem:(id <ToDoItems>)anItem
  69. {
  70.     char *buf;
  71.     int len;
  72.     
  73.     [anItem getData:&buf length:&len];
  74.     
  75.     return [self initSubject:[anItem subject] startDate:[anItem startDate]
  76.                 dueDate:[anItem dueDate] completeDate:[anItem dateCompleted]
  77.                 type:[anItem type] isPrivate:[anItem isPrivate]
  78.                 isCompleted:[anItem isCompleted] data:buf dataLen:len];
  79. }
  80.  
  81. - init
  82. {
  83.     return [self initSubject:NULL startDate:time(NULL) dueDate:(time(NULL)+60)
  84.         completeDate:0 type:TODO_TYPE_NORMAL isPrivate:NO isCompleted:NO 
  85.         data:NULL dataLen:0];
  86. }
  87.  
  88. - free
  89. {
  90.     if(subject) {
  91.         NX_FREE(subject);
  92.         subject = NULL;
  93.     }
  94.     if(data) {
  95.         NX_FREE(data);
  96.         data = NULL;
  97.         dataLen = 0;
  98.     }
  99.     
  100.     free(_flags);
  101.     return [super free];
  102. }
  103.  
  104. - (char *)subject                        { return subject; }
  105. - (long)priority                        { return priority; }
  106. - (long)startDate                        { return startDate; }
  107. - (long)dueDate                        { return dueDate; }
  108. - (long)dateCompleted                { return dateCompleted; }
  109. - (char)type                            { return _flags->type; }
  110. - (BOOL)isPrivate                        { return _flags->private; }
  111. - (BOOL)isCompleted                    { return _flags->completed; }
  112.  
  113. - (const char *)asciiStartDate    
  114.     static char buf[40];
  115.     
  116.     if(!startDate) {
  117.         return NULL;
  118.     }
  119.     
  120.     strftime(buf, 40, "%m/%d/%y", localtime(&startDate));
  121.  
  122.     return buf;
  123. }
  124.  
  125. - (const char *)asciiCompletedDate    
  126.     static char buf[40];
  127.     
  128.     if(!dateCompleted) {
  129.         return NULL;
  130.     }
  131.     
  132.     strftime(buf, 40, "%m/%d/%y", localtime(&dateCompleted));
  133.  
  134.     return buf;
  135. }
  136.  
  137. - (const char *)asciiDueDate        
  138.     static char buf[40];
  139.     
  140.     switch(_flags->type) {
  141.         case TODO_TYPE_NORMAL :
  142.         case TODO_TYPE_APPOINTMENT :
  143.             strftime(buf, 40, "%m/%d/%y", localtime(&dueDate));
  144.             break;
  145.         case TODO_TYPE_HIGHPRIORITY :
  146.             sprintf(buf, "ASAP!");
  147.             break;
  148.         case TODO_TYPE_LOWPRIORITY :
  149.             sprintf(buf, "LATER");
  150.             break;
  151.     }
  152.     
  153.     return buf;
  154. }
  155.  
  156. - getData:(char **)d length:(int *)len
  157. {
  158.     *d = data;
  159.     *len = dataLen;
  160.     return self;
  161. }
  162.  
  163. - setSubject:(const char *)s
  164. {
  165.     subject = RZReplaceStringBuffer(&subject, s);
  166.      return self;
  167. }
  168.  
  169. - setPriority:(long)p
  170. {
  171.     priority = p;
  172.    return self;
  173. }
  174.  
  175. - setStartDate:(long)newDate
  176. {
  177.     startDate = newDate;
  178.    return self;
  179. }
  180.  
  181. - setDueDate:(long)newDate
  182. {
  183.     dueDate = newDate;
  184.     [self adjustPriority];
  185.    return self;
  186. }
  187.  
  188. - setType:(char)newType
  189. {
  190.     _flags->type = newType;
  191.     priority = 0;
  192.  
  193.     return self;
  194. }
  195.  
  196. - setPrivate:(BOOL)flag
  197. {
  198.     _flags->private = flag;
  199.     return self;
  200. }
  201.  
  202. - setCompleted:(BOOL)flag
  203. {
  204.     _flags->completed = flag;
  205.     dateCompleted = flag ? time(NULL) : 0;
  206.     return self;
  207. }
  208.  
  209. - setData:(const char *)d
  210. {
  211.     RZReplaceStringBuffer(&data, d);
  212.     dataLen = data ? strlen(data) + 1 : 0;
  213.    return self;
  214. }
  215.  
  216. - setDataFromText:aText
  217. {
  218.     NXStream *stream;
  219.     BOOL failed = NO;
  220.     
  221.     stream = NXOpenMemory(NULL, 0, NX_READWRITE);
  222.     NX_DURING
  223.     [aText writeRichText:stream];
  224.     NX_HANDLER
  225.     failed = YES;
  226.     NX_ENDHANDLER
  227.     if(!failed) {
  228.         [self setDataFromStream:stream];
  229.         NXCloseMemory(stream, NX_FREEBUFFER);
  230.     }
  231.     
  232.    return self;
  233. }
  234.  
  235. - setDataFromStream:(NXStream *)stream
  236. {
  237.     char *_data;
  238.     int _dataLen, _maxLen;
  239.     
  240.     NXGetMemoryBuffer(stream, &_data, &_dataLen, &_maxLen);
  241.     [self setData:_data];
  242.     
  243.    return self;
  244. }
  245.  
  246. - writeToStream:(NXStream *)stream
  247. {
  248.     static id dummyText = nil;
  249.     
  250.     NXPrintf(stream, "Subject: %s\n",[self subject]);
  251.     switch(_flags->type) {
  252.         case TODO_TYPE_NORMAL :
  253.             NXPrintf(stream, "Type: Normal\n");
  254.             break;
  255.             
  256.         case TODO_TYPE_APPOINTMENT :
  257.             NXPrintf(stream, "Type: Appointment\n");
  258.             break;
  259.             
  260.         case TODO_TYPE_LOWPRIORITY : 
  261.             NXPrintf(stream, "Type: Low Priority\n");
  262.             break;
  263.             
  264.         case TODO_TYPE_HIGHPRIORITY :
  265.             NXPrintf(stream, "Type: High Priority\n");
  266.             break;
  267.     }
  268.     NXPrintf(stream, "Created: %s\n",[self asciiStartDate]);
  269.     NXPrintf(stream, "Due on: %s\n",[self asciiDueDate]);
  270.     if(_flags->completed) {
  271.         NXPrintf(stream, "Completed: %s\n",[self asciiCompletedDate]);
  272.     }
  273.     NXPrintf(stream, "\n");
  274.     if(dataLen) {
  275.         NXStream *_stream;
  276.         if(!dummyText) {
  277.             dummyText = [[Text alloc] init];
  278.         }
  279.         _stream = NXOpenMemory(NULL, 0, NX_READWRITE);
  280.         NXWrite(_stream, data, dataLen);
  281.         NXSeek(_stream, 0, NX_FROMSTART);
  282.         [dummyText readRichText:_stream];
  283.         NXCloseMemory(_stream, NX_FREEBUFFER);
  284.         [dummyText writeText:stream];
  285.     }
  286.     return self;
  287. }
  288.  
  289. #define LOW_PRIORITY_BASE 0
  290. #define LOW_PRIORITY_SIZE 256
  291. #define LOW_PRIORITY_MAX (LOW_PRIORITY_BASE + LOW_PRIORITY_SIZE)
  292. #define NORMAL_PRIORITY_BASE (LOW_PRIORITY_MAX + 1)
  293. #define NORMAL_PRIORITY_SIZE 512
  294. #define NORMAL_PRIORITY_MAX (NORMAL_PRIORITY_BASE + NORMAL_PRIORITY_SIZE)
  295. #define HIGH_PRIORITY_BASE (NORMAL_PRIORITY_MAX + 1)
  296. #define HIGH_PRIORITY_SIZE 256
  297. #define HIGH_PRIORITY_MAX (HIGH_PRIORITY_BASE + HIGH_PRIORITY_SIZE)
  298.  
  299. #define ONE_DAY (60*60*24)
  300. #define ONE_YEAR (60*60*24*365)
  301. #define OLDEST_ITEM ONE_YEAR
  302.     
  303. - adjustPriority
  304. /* 
  305.  * priority quantizer - I've decided to quantize priorities in the
  306.  * range 0 - 1023 (if you have more than 1K of stuff to do, you're
  307.  * in trouble :-)  The priority space is divided into three regions:
  308.  *     0 - 255        LOW PRIORITY ITEMS
  309.  *        256 - 767    NORMAL PRIORITY ITEMS
  310.  *        768 - 1023     HIGH PRIORITY ITEMS
  311.  */
  312. {
  313.     long age = time(NULL) - startDate;
  314.     long deadline = dueDate - time(NULL);
  315.     
  316.     if(_flags->completed) {
  317.         priority = -dateCompleted;
  318.         return self;
  319.     }
  320.         
  321.     switch(_flags->type) {
  322.         case TODO_TYPE_LOWPRIORITY : 
  323.             /* accrue priority over time but never get above LOW_PRIORITY_MAX */
  324.             priority = LOW_PRIORITY_BASE + 
  325.                 LOW_PRIORITY_SIZE*((float)age/(float)OLDEST_ITEM);
  326.             priority = MIN(priority, LOW_PRIORITY_MAX);
  327.             break;
  328.             
  329.         case TODO_TYPE_NORMAL :
  330.             /* increase priority as we get nearer the deadline */
  331.             if(deadline > 0) {
  332.                 /* quantize the time until the deadline */
  333.                 priority = NORMAL_PRIORITY_MAX - 
  334.                     NORMAL_PRIORITY_SIZE*((float)deadline/(float)OLDEST_ITEM);
  335.             } else {
  336.                 /* past the due date, make max priority */
  337.                 priority = NORMAL_PRIORITY_MAX;
  338.             }
  339.             priority = MIN(priority, NORMAL_PRIORITY_MAX);
  340.             break;
  341.  
  342.         case TODO_TYPE_HIGHPRIORITY :
  343.             /* accrue priority over time but never get above HIGH_PRIORITY_MAX */
  344.             priority = HIGH_PRIORITY_BASE + 
  345.                 HIGH_PRIORITY_SIZE*((float)age/(float)OLDEST_ITEM);
  346.             priority = MIN(priority, HIGH_PRIORITY_MAX);
  347.             break;
  348.  
  349.         case TODO_TYPE_APPOINTMENT :
  350.             /* drop into an appropriate slot based on the appointment date */
  351.             if(deadline > 10*ONE_DAY) {
  352.                 priority = LOW_PRIORITY_MAX;
  353.             } else if(deadline < 2*ONE_DAY) {
  354.                 priority = HIGH_PRIORITY_MAX;
  355.             } else {
  356.                 priority = NORMAL_PRIORITY_MAX;
  357.             }
  358.             break;
  359.  
  360.     }
  361.         
  362.     return self;
  363. }
  364.  
  365. - awake
  366. {
  367.     [self adjustPriority];
  368.     return self;
  369. }
  370.  
  371. - read:(NXTypedStream *)ts
  372. {
  373.     [super read:ts];
  374.     _flags = (toDoItemFlags *)calloc(1,sizeof(toDoItemFlags));
  375.     
  376.     switch(NXTypedStreamClassVersion(ts, [[self class] name])) {
  377.         case 4 :
  378.             NXReadTypes(ts, "*llll*iI",
  379.                 &subject, &priority, &startDate, &dueDate, &dateCompleted, 
  380.                 &data, &dataLen, _flags);
  381.             break;    
  382.             
  383.         case 3 :
  384.             NXReadTypes(ts, "*llll*ii",
  385.                 &subject, &priority, &startDate, &dueDate, &dateCompleted, 
  386.                 &data, &dataLen, _flags);
  387.             break;    
  388.             
  389.         case 2 :
  390.             NXReadTypes(ts, "*lll*ii",
  391.                 &subject, &priority, &startDate, &dueDate, &data, &dataLen, _flags);
  392.             break;    
  393.             
  394.         case 1 :
  395.             NXReadTypes(ts, "*lll*ic",
  396.                 &subject, &priority, &startDate, &dueDate, &data, &dataLen, &_flags->type);
  397.             break;    
  398.             
  399.         case 0 :
  400.             NXReadTypes(ts, "*lll*i",
  401.                 &subject, &priority, &startDate, &dueDate, &data, &dataLen);
  402.             break;
  403.     }
  404.     
  405.     return self;
  406. }
  407.  
  408. - write:(NXTypedStream *)ts
  409. {
  410.     [super write:ts];
  411.     NXWriteTypes(ts, "*llll*iI",
  412.         &subject, &priority, &startDate, &dueDate, &dateCompleted, 
  413.         &data, &dataLen, _flags);
  414.     return self;
  415. }
  416.  
  417. @end
  418.